﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Enter.DataCommon
{
    /// <summary>
    /// 对象容器，用来支持DataCommen内部使用的缓存对象。
    /// </summary>
    public class ObjectsContainer
    {
        private Dictionary<string, IDictionary> _RegionObjects;
        private object _SyncObject;

        public static readonly ObjectsContainer Default;

        static ObjectsContainer()
        {
            Default = new ObjectsContainer();
        }
        /// <summary>
        /// 初始化一个对象容器。
        /// </summary>
        public ObjectsContainer()
        {
            _SyncObject = new object();
            _RegionObjects = new Dictionary<string, IDictionary>();
        }

        private void SutValue<TKey, TValue>(IDictionary<TKey, TValue> container, TKey key, TValue value)
        {
            lock (container)
            {
                container[key] = value;
            }
        }
        private TValue GetValue<TKey, TValue>(IDictionary<TKey, TValue> container, TKey key)
        {
            return GetValue<TKey, TValue>(container, key, default(TValue));
        }
        private TValue GetValue<TKey, TValue>(IDictionary<TKey, TValue> container, TKey key, TValue defaultVal)
        {
            TValue val;
            if (container.TryGetValue(key, out val))
                return val;
            else
                return defaultVal;
        }

        /// <summary>
        /// 向容器中添加值
        /// </summary>
        /// <typeparam name="TKey">键的类型</typeparam>
        /// <typeparam name="TValue">值的类型</typeparam>
        /// <param name="region">对进行分区存放Key</param>
        /// <param name="key">用于标识value的键</param>
        /// <param name="value">交给容器保管的值</param>
        public void Put<TKey, TValue>(string region, TKey key, TValue value)
        {
            // 获取一个容器
            Dictionary<TKey, TValue> container = GetValue<string, IDictionary>(_RegionObjects, region) as Dictionary<TKey, TValue>;
            if (container == null)
            {
                // 创建一个新容器（容器的名称为依据参数：region）
                lock (_RegionObjects)
                {
                    container = GetValue<string, IDictionary>(_RegionObjects, region) as Dictionary<TKey, TValue>;
                    if (container == null)
                    {
                        container = new Dictionary<TKey, TValue>();
                        SutValue<string, IDictionary>(_RegionObjects, region, container);
                    }
                }
            }
            // 修改或向容器中添加一组键值对
            lock (container)
            {
                container[key] = value;
            }
        }
        /// <summary>
        /// 依据键获取容器保管的值
        /// </summary>
        /// <typeparam name="TKey">键的类型</typeparam>
        /// <typeparam name="TValue">值的类型</typeparam>
        /// <param name="region">键所在的区域</param>
        /// <param name="key">标识值的惟一键</param>
        /// <returns>获取到的值，如果不存在则返回类型[TValue]的默认值</returns>
        public TValue Get<TKey, TValue>(string region, TKey key)
        {
            Dictionary<TKey, TValue> container = GetValue<string, IDictionary>(_RegionObjects, region) as Dictionary<TKey, TValue>;
            if (container == null)
                return default(TValue);
            else
                return GetValue<TKey, TValue>(container, key);
        }
        /// <summary>
        /// 依据键获取容器保管的值
        /// </summary>
        /// <typeparam name="TKey">键的类型</typeparam>
        /// <typeparam name="TValue">值的类型</typeparam>
        /// <param name="region">键所在的区域</param>
        /// <param name="key">标识值的惟一键</param>
        /// <param name="defaultVal">当key不存在时，返回一个指定的默认值。</param>
        /// <returns>获取到的值，如果不存在则返回类型[TValue]的默认值</returns>
        public TValue Get<TKey, TValue>(string region, TKey key, TValue defaultVal)
        {
            Dictionary<TKey, TValue> container = GetValue<string, IDictionary>(_RegionObjects, region) as Dictionary<TKey, TValue>;
            if (container == null)
                return defaultVal;
            else
                return GetValue<TKey, TValue>(container, key, defaultVal);
        }
        /// <summary>
        /// 依据键获取容器保管的值
        /// </summary>
        /// <typeparam name="TKey">键的类型</typeparam>
        /// <typeparam name="TValue">值的类型</typeparam>
        /// <param name="region">键所在的区域</param>
        /// <param name="key">标识值的惟一键</param>
        /// <returns>获取到的值，如果不存在则返回类型[TValue]的默认值</returns>
        public TValue Get<TKey, TValue>(string region, TKey key, Func<TValue> getValue)
        {
            IDictionary<TKey, TValue> container = GetValue<string, IDictionary>(_RegionObjects, region) as Dictionary<TKey, TValue>;
            if (container == null || container.ContainsKey(key) == false)
            {
                lock (_SyncObject)
                {
                    container = GetValue<string, IDictionary>(_RegionObjects, region) as Dictionary<TKey, TValue>;
                    if (container == null || container.ContainsKey(key) == false)
                    {
                        TValue value = getValue();
                        Put<TKey, TValue>(region, key, value);
                        return value;
                    }
                    else
                    {
                        return GetValue<TKey, TValue>(container, key);
                    }
                }
            }
            else
            {
                return GetValue<TKey, TValue>(container, key);
            }
        }
        /// <summary>
        /// 获取指定区域内所有的值
        /// </summary>
        /// <typeparam name="TValue">值的类型</typeparam>
        /// <param name="region">指定区域</param>
        /// <returns>返回TValue的集合</returns>
        public IEnumerable<TValue> Get<TValue>(string region)
        {
            IDictionary container = GetValue<string, IDictionary>(_RegionObjects, region);
            return container.Values.Cast<TValue>();
        }
        /// <summary>
        /// 移除容器中的指定区域
        /// </summary>
        /// <param name="region">区域名称</param>
        public void Remove(string region)
        {
            if (_RegionObjects.ContainsKey(region))
            {
                lock (_RegionObjects)
                {
                    if (_RegionObjects.ContainsKey(region))
                        _RegionObjects.Remove(region);
                }
            }
        }
    }
}
